package com.yitiao.config;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionLocator;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Mono;
import java.util.List;

/**
 * 注册监听器，监听 nacos 的路由变化。
 */
@Service
@Slf4j
public class GatewayRouteHandler implements ApplicationEventPublisherAware {
    // 对路由的写操作  - 新增、删除
    private final RouteDefinitionWriter writer;
    // 对路由的读操作  - 查询
    private final RouteDefinitionLocator locator;

    public GatewayRouteHandler(RouteDefinitionWriter writer, RouteDefinitionLocator locator) {
        this.writer = writer;
        this.locator = locator;
    }

    private ApplicationEventPublisher eventPublisher;

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.eventPublisher = applicationEventPublisher;
    }

    /**
     * <h2>增加路由定义</h2>
     */
    public void addRoute(RouteDefinition definition) {

        log.info("gateway add route: [{}]", definition);

        // 保存路由配置并发布
        writer.save(Mono.just(definition)).subscribe();
        // 发布事件通知给 Gateway, 同步新增的路由定义
        this.eventPublisher.publishEvent(new RefreshRoutesEvent(this));

    }

    public void BatchAddRoute(List<RouteDefinition> definitions) {
        definitions.forEach(this::addRoute);
    }


    /**
     * <h2>全量更新路由</h2>
     */
    public void fullUpdateRoute(List<RouteDefinition> definitions) {

        log.info("gateway full update route: [{}]", definitions);

        // 先拿到当前 Gateway 中存储的路由定义
        List<RouteDefinition> routeDefinitionsExits = locator.getRouteDefinitions().buffer().blockFirst();
        if (!CollectionUtils.isEmpty(routeDefinitionsExits)) {
            // 清除掉之前所有的 "旧的" 路由定义
            routeDefinitionsExits.forEach(rd -> {
                log.info("delete route definition: [{}]", rd);
                deleteRouteById(rd.getId());
            });
        }

        // 把更新的路由定义同步到 gateway 中
        definitions.forEach(this::addRoute);
    }

    /**
     * <h2>根据路由 id 删除路由配置</h2>
     */
    private void deleteRouteById(String id) {

        try {
            writer.delete(Mono.just(id)).subscribe();
            // 发布事件通知给 gateway 更新路由定义
            this.eventPublisher.publishEvent(new RefreshRoutesEvent(this));
            log.info("gateway delete route success - id: [{}]", id);
        } catch (Exception ex) {
            log.error("gateway delete route fail: [{}]", ex.getMessage(), ex);
        }
    }

    /**
     * <h2>单个更新路由</h2>
     */
    private void updateRouteById(RouteDefinition definition) {
        try {
            deleteRouteById(definition.getId());
            addRoute(definition);
        } catch (Exception ex) {
            log.error("gateway update route fail: [{}]", ex.getMessage(), ex);
        }
    }

    private void batchUpdateRoute(List<RouteDefinition> definitions) {
        definitions.forEach(this::updateRouteById);
    }
}
